home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / SimpleClientServer / SimpleClient / simpleclient.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  28.3 KB  |  741 lines

  1. //----------------------------------------------------------------------------
  2. // File: SimpleClient.cpp
  3. //
  4. // Desc: The SimpleClientServer sample is a simple client/server application. 
  5. //
  6. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #define STRICT
  9. #include <windows.h>
  10. #include <basetsd.h>
  11. #include <dplay8.h>
  12. #include <dpaddr.h>
  13. #include <dplobby8.h>
  14. #include <dxerr8.h>
  15. #include <tchar.h>
  16. #include "DXUtil.h"
  17. #include "SimpleClientServer.h"
  18. #include "NetClient.h"
  19. #include "resource.h"
  20.  
  21.  
  22.  
  23.  
  24. //-----------------------------------------------------------------------------
  25. // player struct locking defines
  26. //-----------------------------------------------------------------------------
  27. CRITICAL_SECTION g_csPlayerContext;
  28. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  29. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  30. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) DestoryPlayerStruct( pPlayerInfo ); } pPlayerInfo = NULL;
  31. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  32.  
  33.  
  34. //-----------------------------------------------------------------------------
  35. // Defines, and constants
  36. //-----------------------------------------------------------------------------
  37. struct APP_PLAYER_INFO
  38. {
  39.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  40.                                             // are done w/ this object
  41.     DPNID dpnidPlayer;                      // DPNID of player
  42.     TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
  43.  
  44.     APP_PLAYER_INFO* pNext;
  45.     APP_PLAYER_INFO* pPrev;
  46. };
  47.  
  48.  
  49.  
  50.  
  51. //-----------------------------------------------------------------------------
  52. // Global variables
  53. //-----------------------------------------------------------------------------
  54. IDirectPlay8Client*     g_pDPClient                   = NULL;    // DirectPlay peer object
  55. CNetClientWizard*       g_pNetClientWizard            = NULL;    // Connection wizard
  56. IDirectPlay8LobbiedApplication* g_pLobbiedApp         = NULL;    // DirectPlay lobbied app 
  57. BOOL                    g_bWasLobbyLaunched           = FALSE;   // TRUE if lobby launched
  58. HINSTANCE               g_hInst                       = NULL;    // HINST of app
  59. HWND                    g_hDlg                        = NULL;    // HWND of main dialog
  60. LONG                    g_lNumberOfActivePlayers      = 0;       // Number of players currently in game
  61. DPNID                   g_dpnidLocalPlayer            = 0;
  62. APP_PLAYER_INFO         g_playerHead;
  63. TCHAR                   g_strAppName[256]             = TEXT("SimpleClient");
  64. HRESULT                 g_hrDialog;                              // Exit code for app 
  65. TCHAR                   g_strLocalPlayerName[MAX_PATH];          // Local player name
  66.  
  67.  
  68.  
  69.  
  70. //-----------------------------------------------------------------------------
  71. // Function-prototypes
  72. //-----------------------------------------------------------------------------
  73. HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  74. HRESULT WINAPI   DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  75. INT_PTR CALLBACK GreetingDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  76. HRESULT  InitDirectPlay();
  77. HRESULT  WaveToAllPlayers();
  78. VOID     AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine );
  79. HRESULT  GetPlayerStruct( DPNID dpnidPlayer, APP_PLAYER_INFO** ppPlayerInfo );
  80. VOID     DestoryPlayerStruct( APP_PLAYER_INFO* pPlayerInfo );
  81. VOID     AddPlayerStruct( APP_PLAYER_INFO* pPlayerInfo );
  82.  
  83.  
  84.  
  85.  
  86. //-----------------------------------------------------------------------------
  87. // Name: WinMain()
  88. // Desc: Entry point for the application.  Since we use a simple dialog for 
  89. //       user interaction we don't need to pump messages.
  90. //-----------------------------------------------------------------------------
  91. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  92.                       LPSTR pCmdLine, INT nCmdShow )
  93. {
  94.     HRESULT hr;
  95.     HKEY    hDPlaySampleRegKey;
  96.     BOOL    bConnectSuccess = FALSE;
  97.  
  98.     ZeroMemory( &g_playerHead, sizeof(APP_PLAYER_INFO) );
  99.     g_playerHead.pNext = &g_playerHead;
  100.     g_playerHead.pPrev = &g_playerHead;
  101.  
  102.     g_hInst = hInst; 
  103.     InitializeCriticalSection( &g_csPlayerContext );
  104.  
  105.     // Read persistent state information from registry
  106.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  107.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  108.                     &hDPlaySampleRegKey, NULL );
  109.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  110.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  111.  
  112.     // Init COM so we can use CoCreateInstance
  113.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  114.  
  115.     // Create helper class
  116.     g_pNetClientWizard = new CNetClientWizard( hInst, g_strAppName, &g_guidApp );
  117.  
  118.     if( FAILED( hr = InitDirectPlay() ) )
  119.     {
  120.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  121.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  122.                     TEXT("The sample will now quit."),
  123.                     g_strAppName, MB_OK | MB_ICONERROR );
  124.         return FALSE;
  125.     }
  126.  
  127.     // If we were launched from a lobby client, then we may have connection settings
  128.     // that we can use to connect to the host.  If not, then we'll need to prompt 
  129.     // the user to detrimine how to connect.
  130.     if( g_bWasLobbyLaunched && g_pNetClientWizard->HaveConnectionSettingsFromLobby() )
  131.     {
  132.         // If were lobby launched then the DPL_MSGID_CONNECT has already been
  133.         // handled, and since the lobby client also sent us connection settings
  134.         // we can use them to either host or join a DirectPlay session. 
  135.         if( FAILED( hr = g_pNetClientWizard->ConnectUsingLobbySettings() ) )
  136.         {
  137.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  138.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  139.                         TEXT("The sample will now quit."),
  140.                         g_strAppName, MB_OK | MB_ICONERROR );
  141.  
  142.             bConnectSuccess = FALSE;
  143.         }
  144.         else
  145.         {
  146.             // Read information from g_pNetClientWizard
  147.             _tcscpy( g_strLocalPlayerName, g_pNetClientWizard->GetPlayerName() );
  148.  
  149.             bConnectSuccess = TRUE; 
  150.         }
  151.     }
  152.     else
  153.     {
  154.         // If not lobby launched, prompt the user about the network 
  155.         // connection and which session they would like to join or 
  156.         // if they want to create a new one.
  157.  
  158.         // Setup connection wizard
  159.         g_pNetClientWizard->SetPlayerName( g_strLocalPlayerName );
  160.  
  161.         // Start a connection wizard.  The wizard uses GDI dialog boxes.
  162.         // More complex games can use this as a starting point and add a 
  163.         // fancier graphics layer such as Direct3D.
  164.         hr = g_pNetClientWizard->DoConnectWizard();        
  165.         if( FAILED( hr ) ) 
  166.         {
  167.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  168.             MessageBox( NULL, TEXT("Multiplayer connect failed. ")
  169.                         TEXT("The sample will now quit."),
  170.                         g_strAppName, MB_OK | MB_ICONERROR );
  171.             bConnectSuccess = FALSE;
  172.         } 
  173.         else if( hr == NCW_S_QUIT ) 
  174.         {
  175.             // The user canceled the Multiplayer connect, so quit 
  176.             bConnectSuccess = FALSE;
  177.         }
  178.         else
  179.         {
  180.             bConnectSuccess = TRUE; 
  181.  
  182.             // Read information from g_pNetClientWizard
  183.             _tcscpy( g_strLocalPlayerName, g_pNetClientWizard->GetPlayerName() );
  184.  
  185.             // Write information to the registry
  186.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  187.         }
  188.     }
  189.  
  190.     if( bConnectSuccess )
  191.     {
  192.         // App is now connected via DirectPlay, so start the game.  
  193.  
  194.         // For this sample, we just start a simple dialog box game.
  195.         g_hrDialog = S_OK;
  196.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, 
  197.                    (DLGPROC) GreetingDlgProc );
  198.  
  199.         if( FAILED( g_hrDialog ) )
  200.         {
  201.             if( g_hrDialog == DPNERR_CONNECTIONLOST )
  202.             {
  203.                 MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  204.                             TEXT("The sample will now quit."),
  205.                             g_strAppName, MB_OK | MB_ICONERROR );
  206.             }
  207.             else
  208.             {
  209.                 DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  210.                 MessageBox( NULL, TEXT("An error occured during the game. ")
  211.                             TEXT("The sample will now quit."),
  212.                             g_strAppName, MB_OK | MB_ICONERROR );
  213.             }
  214.         }
  215.     }
  216.  
  217.     // Cleanup DirectPlay and helper classes
  218.     if( g_pDPClient )
  219.     {
  220.         g_pDPClient->Close(0);
  221.         SAFE_RELEASE( g_pDPClient );
  222.     }
  223.  
  224.     if( g_pLobbiedApp )
  225.     {
  226.         g_pLobbiedApp->Close( 0 );
  227.         SAFE_RELEASE( g_pLobbiedApp );
  228.     }
  229.  
  230.     // Don't delete the wizard until we know that 
  231.     // DirectPlay is out of its message handlers.
  232.     // This will be true after Close() has been called. 
  233.     SAFE_DELETE( g_pNetClientWizard );
  234.  
  235.     RegCloseKey( hDPlaySampleRegKey );
  236.     DeleteCriticalSection( &g_csPlayerContext );
  237.     CoUninitialize();
  238.  
  239.     return TRUE;
  240. }
  241.  
  242.  
  243.  
  244.  
  245. //-----------------------------------------------------------------------------
  246. // Name: InitDirectPlay()
  247. // Desc: 
  248. //-----------------------------------------------------------------------------
  249. HRESULT InitDirectPlay()
  250. {
  251.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  252.     HRESULT hr;
  253.  
  254.     // Create IDirectPlay8Client
  255.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Client, NULL, 
  256.                                        CLSCTX_INPROC_SERVER,
  257.                                        IID_IDirectPlay8Client, 
  258.                                        (LPVOID*) &g_pDPClient ) ) )
  259.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  260.  
  261.     // Create IDirectPlay8LobbiedApplication
  262.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  263.                                        CLSCTX_INPROC_SERVER,
  264.                                        IID_IDirectPlay8LobbiedApplication, 
  265.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  266.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  267.  
  268.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  269.     g_pNetClientWizard->Init( g_pDPClient, g_pLobbiedApp );
  270.  
  271.     // Init IDirectPlay8Client
  272.     if( FAILED( hr = g_pDPClient->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  273.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  274.  
  275.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  276.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  277.     // so be prepared ahead of time.
  278.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  279.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  280.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  281.  
  282.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  283.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  284.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  285.     // multiple lobby connections, we do not need to remember the lobby connection
  286.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  287.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  288.  
  289.     return S_OK;
  290. }
  291.  
  292.  
  293.  
  294.  
  295. //-----------------------------------------------------------------------------
  296. // Name: GreetingDlgProc()
  297. // Desc: Handles dialog messages
  298. //-----------------------------------------------------------------------------
  299. INT_PTR CALLBACK GreetingDlgProc( HWND hDlg, UINT msg, 
  300.                                   WPARAM wParam, LPARAM lParam )
  301. {
  302.     switch( msg ) 
  303.     {
  304.         case WM_INITDIALOG:
  305.         {
  306.             g_hDlg = hDlg;
  307.  
  308.             // Load and set the icon
  309.             HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  310.             SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  311.             SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  312.  
  313.             SetWindowText( hDlg, g_strAppName );
  314.  
  315.             // Display local player's name
  316.             SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  317.  
  318.             PostMessage( hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  319.             break;
  320.         }
  321.  
  322.         case WM_APP_UPDATE_STATS:
  323.         {
  324.             // Update the number of players in the game
  325.             TCHAR strNumberPlayers[32];
  326.  
  327.             wsprintf( strNumberPlayers, TEXT("%d"), g_lNumberOfActivePlayers );
  328.             SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  329.             break;
  330.         }
  331.  
  332.         case WM_APP_DISPLAY_WAVE:
  333.         {
  334.             HRESULT          hr;
  335.             DPNID            dpnidPlayer = (DWORD)wParam;
  336.             APP_PLAYER_INFO* pPlayerInfo = NULL;
  337.             
  338.             // Get the player struct accosicated with this DPNID
  339.             PLAYER_LOCK(); // enter player struct CS
  340.             hr = GetPlayerStruct( dpnidPlayer, &pPlayerInfo );
  341.             PLAYER_UNLOCK(); // leave player struct CS
  342.  
  343.             if( FAILED(hr) || pPlayerInfo == NULL )
  344.             {
  345.                 // The player who sent this may have gone away before this 
  346.                 // message was handled, so just ignore it
  347.                 break;
  348.             }
  349.             
  350.             // Make wave message and display it.
  351.             TCHAR szWaveMessage[MAX_PATH];
  352.             wsprintf( szWaveMessage, TEXT("%s just waved at you, %s!\r\n"), 
  353.                       pPlayerInfo->strPlayerName, g_strLocalPlayerName );
  354.  
  355.             PLAYER_LOCK();
  356.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  357.             PLAYER_UNLOCK();
  358.  
  359.             AppendTextToEditControl( hDlg, szWaveMessage );
  360.             break;
  361.         }
  362.  
  363.         case WM_COMMAND:
  364.         {
  365.             switch( LOWORD(wParam) )
  366.             {
  367.                 case IDC_WAVE:
  368.                     if( FAILED( g_hrDialog = WaveToAllPlayers() ) )
  369.                     {
  370.                         DXTRACE_ERR( TEXT("WaveToAllPlayers"), g_hrDialog );
  371.                         EndDialog( hDlg, 0 );
  372.                     }
  373.  
  374.                     return TRUE;
  375.  
  376.                 case IDCANCEL:
  377.                     g_hrDialog = S_OK;
  378.                     EndDialog( hDlg, 0 );
  379.                     return TRUE;
  380.             }
  381.             break;
  382.         }
  383.     }
  384.  
  385.     return FALSE; // Didn't handle message
  386. }
  387.  
  388.  
  389.  
  390.  
  391. //-----------------------------------------------------------------------------
  392. // Name: DirectPlayMessageHandler
  393. // Desc: Handler for DirectPlay messages.  This function is called by
  394. //       the DirectPlay message handler pool of threads, so be careful of thread
  395. //       synchronization problems with shared memory
  396. //-----------------------------------------------------------------------------
  397. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  398.                                          DWORD dwMessageId, 
  399.                                          PVOID pMsgBuffer )
  400. {
  401.     // Try not to stay in this message handler for too long, otherwise
  402.     // there will be a backlog of data.  The best solution is to 
  403.     // queue data as it comes in, and then handle it on other threads.
  404.     
  405.     // This function is called by the DirectPlay message handler pool of 
  406.     // threads, so be careful of thread synchronization problems with shared memory
  407.  
  408.     switch( dwMessageId )
  409.     {
  410.         case DPN_MSGID_TERMINATE_SESSION:
  411.         {
  412.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  413.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  414.  
  415.             g_hrDialog = DPNERR_CONNECTIONLOST;
  416.             EndDialog( g_hDlg, 0 );
  417.             break;
  418.         }
  419.  
  420.         case DPN_MSGID_RECEIVE:
  421.         {
  422.             HRESULT hr;
  423.             PDPNMSG_RECEIVE pReceiveMsg;
  424.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  425.  
  426.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  427.             switch( pMsg->dwType )
  428.             {
  429.                 case GAME_MSGID_SET_ID:
  430.                 {
  431.                     // The host is tell us the DPNID for this client
  432.                     GAMEMSG_SET_ID* pSetIDMsg;
  433.                     pSetIDMsg = (GAMEMSG_SET_ID*)pReceiveMsg->pReceiveData;
  434.  
  435.                     g_dpnidLocalPlayer = pSetIDMsg->dpnidPlayer;
  436.                     break;
  437.                 }
  438.  
  439.                 case GAME_MSGID_CREATE_PLAYER:
  440.                 {
  441.                     // The host is telling us about a new player 
  442.                     GAMEMSG_CREATE_PLAYER* pCreatePlayerMsg;
  443.                     pCreatePlayerMsg = (GAMEMSG_CREATE_PLAYER*)pReceiveMsg->pReceiveData;
  444.  
  445.                     // Create a new and fill in a APP_PLAYER_INFO
  446.                     APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  447.                     ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  448.                     pPlayerInfo->lRefCount   = 1;
  449.                     pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  450.                     _tcscpy( pPlayerInfo->strPlayerName, pCreatePlayerMsg->strPlayerName );
  451.  
  452.                     PLAYER_LOCK(); // enter player struct CS
  453.                     AddPlayerStruct( pPlayerInfo );
  454.                     PLAYER_UNLOCK(); // leave player struct CS
  455.  
  456.                     // Update the number of active players, and 
  457.                     // post a message to the dialog thread to update the 
  458.                     // UI.  This keeps the DirectPlay message handler 
  459.                     // from blocking
  460.                     InterlockedIncrement( &g_lNumberOfActivePlayers );
  461.  
  462.                     if( g_hDlg != NULL )
  463.                         PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  464.                     break;
  465.                 };
  466.  
  467.                 case GAME_MSGID_DESTROY_PLAYER:
  468.                 {
  469.                     // The host is telling us about a player that's been destroyed
  470.                     APP_PLAYER_INFO*        pPlayerInfo = NULL;
  471.                     GAMEMSG_DESTROY_PLAYER* pDestroyPlayerMsg;
  472.                     pDestroyPlayerMsg = (GAMEMSG_DESTROY_PLAYER*)pReceiveMsg->pReceiveData;
  473.  
  474.                     // Get the player struct accosicated with this DPNID
  475.                     PLAYER_LOCK(); // enter player struct CS
  476.                     hr = GetPlayerStruct( pDestroyPlayerMsg->dpnidPlayer, &pPlayerInfo );
  477.                     PLAYER_UNLOCK(); // leave player struct CS
  478.  
  479.                     if( FAILED(hr) || pPlayerInfo == NULL )
  480.                     {
  481.                         // The player who sent this may have gone away before this 
  482.                         // message was handled, so just ignore it
  483.                         break;
  484.                     }
  485.             
  486.                     // Release the player struct
  487.                     PLAYER_LOCK();                  // enter player struct CS
  488.                     PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  489.                     PLAYER_UNLOCK();                // leave player struct CS
  490.  
  491.                     // Update the number of active players, and 
  492.                     // post a message to the dialog thread to update the 
  493.                     // UI.  This keeps the DirectPlay message handler 
  494.                     // from blocking
  495.                     InterlockedDecrement( &g_lNumberOfActivePlayers );
  496.  
  497.                     if( g_hDlg != NULL )
  498.                         PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  499.                     break;
  500.                 };
  501.  
  502.                 case GAME_MSGID_WAVE:
  503.                 {
  504.                     // The host is telling us that someone waved to this client
  505.                     APP_PLAYER_INFO* pPlayerInfo = NULL;
  506.                     GAMEMSG_WAVE* pWaveMsg;
  507.                     pWaveMsg = (GAMEMSG_WAVE*)pReceiveMsg->pReceiveData;
  508.  
  509.                     // Ignore wave messages set by the local player
  510.                     if( pWaveMsg->dpnidPlayer == g_dpnidLocalPlayer )
  511.                         break;
  512.  
  513.                     // Get the player struct accosicated with this DPNID
  514.                     PLAYER_LOCK(); // enter player struct CS
  515.                     hr = GetPlayerStruct( pWaveMsg->dpnidPlayer, &pPlayerInfo );
  516.                     PLAYER_ADDREF( pPlayerInfo ); // addref player, since we are using it now
  517.                     PLAYER_UNLOCK(); // leave player struct CS
  518.  
  519.                     if( FAILED(hr) || pPlayerInfo == NULL )
  520.                     {
  521.                         // The player who sent this may have gone away before this 
  522.                         // message was handled, so just ignore it
  523.                         break;
  524.                     }
  525.  
  526.                     // This message is sent when a player has waved to us, so 
  527.                     // post a message to the dialog thread to update the UI.  
  528.                     // This keeps the DirectPlay threads from blocking, and also
  529.                     // serializes the recieves since DirectPlayMessageHandler can
  530.                     // be called simultaneously from a pool of DirectPlay threads.
  531.                     PostMessage( g_hDlg, WM_APP_DISPLAY_WAVE, pPlayerInfo->dpnidPlayer, 0 );
  532.                     break;
  533.                 };
  534.             }
  535.  
  536.             break;
  537.         }
  538.     }
  539.  
  540.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  541.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  542.     if( g_pNetClientWizard )
  543.         g_pNetClientWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
  544.     
  545.     return S_OK;
  546. }
  547.  
  548.  
  549.  
  550.  
  551. //-----------------------------------------------------------------------------
  552. // Name: DirectPlayLobbyMessageHandler
  553. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  554. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  555. //       thread synchronization problems with shared memory
  556. //-----------------------------------------------------------------------------
  557. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  558.                                               DWORD dwMessageId, 
  559.                                               PVOID pMsgBuffer )
  560. {
  561.     switch( dwMessageId )
  562.     {
  563.         case DPL_MSGID_CONNECT:
  564.         {
  565.             PDPL_MESSAGE_CONNECT pConnectMsg;
  566.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  567.  
  568.             // The CNetConnectWizard will handle this message for us,
  569.             // so there is nothing we need to do here for this simple
  570.             // sample.
  571.             break;
  572.         }
  573.  
  574.         case DPL_MSGID_DISCONNECT:
  575.         {
  576.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  577.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  578.  
  579.             // We should free any data associated with the lobby 
  580.             // client here, but there is none.
  581.             break;
  582.         }
  583.  
  584.         case DPL_MSGID_RECEIVE:
  585.         {
  586.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  587.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  588.  
  589.             // The lobby client sent us data.  This sample doesn't
  590.             // expected data from the client, but it is useful 
  591.             // for more complex apps.
  592.             break;
  593.         }
  594.  
  595.         case DPL_MSGID_CONNECTION_SETTINGS:
  596.         {
  597.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  598.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  599.  
  600.             // The lobby client has changed the connection settings.  
  601.             // This simple sample doesn't handle this, but more complex apps may
  602.             // want to.
  603.             break;
  604.         }
  605.     }
  606.  
  607.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  608.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  609.     if( g_pNetClientWizard )
  610.         return g_pNetClientWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  611.                                                          pMsgBuffer );
  612.     
  613.     return S_OK;
  614. }
  615.  
  616.  
  617.  
  618.  
  619. //-----------------------------------------------------------------------------
  620. // Name: WaveToAllPlayers()
  621. // Desc: Send a app-defined "wave" DirectPlay message to all connected players
  622. //-----------------------------------------------------------------------------
  623. HRESULT WaveToAllPlayers()
  624. {
  625.     // This is called by the dialog UI thread.  This will send a message to all
  626.     // the players or inform the player that there is no one to wave at.
  627.     if( g_lNumberOfActivePlayers == 1 )
  628.     {
  629.         MessageBox( NULL, TEXT("No one is around to wave at! :("), 
  630.                     TEXT("SimpleClient"), MB_OK );
  631.     }
  632.     else
  633.     {
  634.         // Send a message to all of the players
  635.         GAMEMSG_GENERIC msgWave;
  636.         msgWave.dwType = GAME_MSGID_WAVE;
  637.  
  638.         DPN_BUFFER_DESC bufferDesc;
  639.         bufferDesc.dwBufferSize = sizeof(GAMEMSG_GENERIC);
  640.         bufferDesc.pBufferData  = (BYTE*) &msgWave;
  641.  
  642.         DPNHANDLE hAsync;
  643.         // DirectPlay will tell via the message handler 
  644.         // if there are any severe errors, so ignore any errors 
  645.         g_pDPClient->Send( &bufferDesc, 1, 0, NULL, &hAsync, DPNSEND_GUARANTEED );
  646.     }
  647.  
  648.     return S_OK;
  649. }
  650.  
  651.  
  652.  
  653.  
  654. //-----------------------------------------------------------------------------
  655. // Name: AppendTextToEditControl()
  656. // Desc: Appends a string of text to the edit control
  657. //-----------------------------------------------------------------------------
  658. VOID AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine )
  659. {
  660.     static TCHAR strText[1024*10];
  661.  
  662.     HWND hEdit = GetDlgItem( hDlg, IDC_LOG_EDIT );
  663.     SendMessage( hEdit, WM_SETREDRAW, FALSE, 0 );
  664.     GetWindowText( hEdit, strText, 1024*9 );
  665.  
  666.     _tcscat( strText, strNewLogLine );
  667.  
  668.     int nSecondLine = 0;
  669.     if( SendMessage( hEdit, EM_GETLINECOUNT, 0, 0 ) > 9 )
  670.         nSecondLine = (int)SendMessage( hEdit, EM_LINEINDEX, 1, 0 );
  671.  
  672.     SetWindowText( hEdit, &strText[nSecondLine] );
  673.  
  674.     SendMessage( hEdit, WM_SETREDRAW, TRUE, 0 );
  675.     InvalidateRect( hEdit, NULL, TRUE );
  676.     UpdateWindow( hEdit );
  677. }
  678.  
  679.  
  680.  
  681.  
  682. //-----------------------------------------------------------------------------
  683. // Name: AddPlayerStruct()
  684. // Desc: Add pPlayerInfo to the circular linked list, g_playerHead
  685. //-----------------------------------------------------------------------------
  686. VOID AddPlayerStruct( APP_PLAYER_INFO* pPlayerInfo )
  687. {
  688.     pPlayerInfo->pNext = g_playerHead.pNext;
  689.     pPlayerInfo->pPrev = &g_playerHead;
  690.  
  691.     g_playerHead.pNext->pPrev = pPlayerInfo;    
  692.     g_playerHead.pNext = pPlayerInfo;    
  693. }
  694.  
  695.  
  696.  
  697.  
  698. //-----------------------------------------------------------------------------
  699. // Name: DestoryPlayerStruct() 
  700. // Desc: Remove pPlayerInfo from the circular linked list, g_playerHead
  701. //-----------------------------------------------------------------------------
  702. VOID DestoryPlayerStruct( APP_PLAYER_INFO* pPlayerInfo )
  703. {
  704.     pPlayerInfo->pNext->pPrev = pPlayerInfo->pPrev;
  705.     pPlayerInfo->pPrev->pNext = pPlayerInfo->pNext;
  706.  
  707.     SAFE_DELETE( pPlayerInfo );
  708. }
  709.  
  710.  
  711.  
  712.  
  713. //-----------------------------------------------------------------------------
  714. // Name: GetPlayerStruct() 
  715. // Desc: Searchs the circular linked list, g_playerHead, for dpnidPlayer
  716. //-----------------------------------------------------------------------------
  717. HRESULT GetPlayerStruct( DPNID dpnidPlayer, APP_PLAYER_INFO** ppPlayerInfo )
  718. {
  719.     if( ppPlayerInfo == NULL )
  720.         return E_FAIL;
  721.  
  722.     APP_PLAYER_INFO* pCurPlayer = g_playerHead.pNext;
  723.     
  724.     *ppPlayerInfo = NULL;
  725.     while ( pCurPlayer != &g_playerHead )
  726.     {
  727.         if( pCurPlayer->dpnidPlayer == dpnidPlayer )
  728.         {
  729.             *ppPlayerInfo = pCurPlayer;
  730.             return S_OK;
  731.         }
  732.             
  733.         pCurPlayer = pCurPlayer->pNext;
  734.     }
  735.  
  736.     // Not found.
  737.     return E_FAIL;
  738. }
  739.  
  740.  
  741.